from bs4 import BeautifulSoup,Tag,PageElement
import json
import requests
import re
from typing import List, Dict, Union, Optional

API_URL="https://u5-creator.s3.game.163.com/manual/pc_md/lua/EggyAPI.html" 

def parse_api_html(html_content: str) -> str:
    """
    解析API文档HTML内容并转换为JSON字符串，限制在指定的DOM结构范围内
    
    参数:
        html_content (str): HTML内容字符串
        
    返回:
        str: 包含解析结果的JSON字符串
    """
    soup = BeautifulSoup(html_content, 'html.parser')
    
    # 限制搜索范围到指定的DOM结构
    app_div = soup.find('div', id='app')
    if not app_div:
        return json.dumps({"error": "未找到id='app'的div"}, ensure_ascii=False)
    
    layout_div = app_div.find('div', class_='Layout')
    if not layout_div:
        return json.dumps({"error": "未找到class='Layout'的div"}, ensure_ascii=False)
    
    vp_content_div = layout_div.find('div', id='VPContent')
    if not vp_content_div:
        return json.dumps({"error": "未找到id='VPContent'的div"}, ensure_ascii=False)
    
    # 解析所有API内容
    return parse_api_content(vp_content_div)

def parse_api_content(content_div) -> str:
    """
    在指定的div范围内解析API内容，只处理函数定义
    """
    results = []
    
    # 查找所有函数定义（h3标签）
    h3_tags = content_div.find_all('h3')
    
    for h3 in h3_tags:
        # 只处理包含函数名的h3标签（包含点号表示函数调用）
        if '.' not in h3.get_text():
            continue
            
        # 获取函数名并去除零宽空格
        function_name = h3.get_text(strip=True).split()[0].replace('\u200b', '')
        
        # 初始化函数定义
        function_def = {
            "function_name": function_name,
            "description": "",
            "signature": "",
            "parameters": [],
            "return": {"type": "", "description": ""}
        }
        
        # 查找描述
        next_sibling = h3.find_next_sibling()
        while next_sibling:
            if next_sibling.name == 'p' and "描述:" in next_sibling.get_text():
                # 去除零宽空格
                description = next_sibling.get_text().replace('\u200b', '')
                function_def["description"] = description.replace("描述:", "").strip()
                break
            next_sibling = next_sibling.find_next_sibling()
        
        # 查找函数签名
        next_sibling = h3.find_next_sibling()
        while next_sibling:
            if next_sibling.name == 'p':
                # 去除零宽空格
                text = next_sibling.get_text().replace('\u200b', '').strip()
                if function_name in text:
                    function_def["signature"] = text
                    break
            next_sibling = next_sibling.find_next_sibling()
        
        # 查找参数列表
        param_header = h3.find_next('h4')
        if param_header:
            table = param_header.find_next('table')
            if table:
                rows = table.find_all('tr')[1:]  # 跳过表头
                for row in rows:
                    cols = row.find_all('td')
                    if len(cols) >= 3:
                        # 去除零宽空格
                        param = {
                            "name": cols[0].get_text(strip=True).replace('\u200b', ''),
                            "type": cols[1].get_text(strip=True).replace('\u200b', ''),
                            "description": cols[2].get_text(strip=True).replace('\u200b', '')
                        }
                        function_def["parameters"].append(param)
        
        # 查找返回类型 - 只在明确存在时解析
        next_sibling = h3.find_next_sibling()
        found_return = False
        while next_sibling:
            # 如果遇到下一个h3标签，停止搜索
            if next_sibling.name == 'h3':
                break
                
            if next_sibling.name == 'p' and "返回类型" in next_sibling.get_text():
                # 去除零宽空格
                text = next_sibling.get_text().replace('\u200b', '')
                text = text.replace("返回类型", "", 1).strip()
                parts = text.split(maxsplit=1)
                if len(parts) > 1:
                    function_def["return"]["type"] = parts[0]
                    function_def["return"]["description"] = parts[1]
                elif parts:
                    function_def["return"]["type"] = parts[0]
                found_return = True
                break
            next_sibling = next_sibling.find_next_sibling()
        
        # 如果没有找到返回类型信息，清除默认值
        if not found_return:
            function_def["return"] = {"type": "", "description": ""}
        
        results.append(function_def)
    
    return json.dumps(results, ensure_ascii=False, indent=2)

# 文件操作函数
def read_html_from_file(file_path: str) -> Optional[str]:
    """从文件读取HTML内容"""
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            return f.read()
    except FileNotFoundError:
        print(f"错误：文件 {file_path} 不存在")
    except PermissionError:
        print(f"错误：没有权限读取文件 {file_path}")
    except Exception as e:
        print(f"读取文件失败：{str(e)}")
    return None


def save_json_to_file(json_str: str, output_path: str) -> bool:
    """将JSON字符串保存到文件"""
    try:
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(json_str)
        print(f"JSON已保存到 {output_path}")
        return True
    except PermissionError:
        print(f"错误：没有权限写入文件 {output_path}")
    except Exception as e:
        print(f"保存文件失败：{str(e)}")
    return False


# URL请求函数
def fetch_and_parse_from_url(url: str, output_file: Optional[str] = None) -> Optional[str]:
    """从URL请求HTML并解析为JSON，可选保存到文件"""
    try:
        # 发送请求（添加超时和User-Agent）
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
        }
        response = requests.get(url, headers=headers, timeout=10)
        response.raise_for_status()  # 抛出HTTP错误状态码
        html_content = response.text
        
        # 解析并返回JSON
        json_result = parse_api_html(html_content)
        
        # 如需保存到文件
        if output_file:
            save_json_to_file(json_result, output_file)
        
        return json_result
    
    except requests.exceptions.HTTPError as e:
        print(f"HTTP请求错误：{str(e)}")
    except requests.exceptions.ConnectionError:
        print("连接错误：无法连接到服务器")
    except requests.exceptions.Timeout:
        print("请求超时")
    except Exception as e:
        print(f"处理URL失败：{str(e)}")
    return None

def start_parse(html,otpt):
    html_content = read_html_from_file(html)
    if html_content:
        json_result = parse_api_html(html_content)
        if json_result:
            save_json_to_file(json_result, otpt)




class APIDocSearcher:
    def __init__(self):
        """初始化API文档搜索器"""
        self.api_docs = []  # 存储所有API文档数据
        self.object_functions = {}  # 存储对象到函数列表的映射
        
    def load_from_json(self, file_path: str):
        """
        从JSON文件加载API文档数据
        
        参数:
            file_path (str): JSON文件路径
        """
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                self.api_docs = json.load(f)
            print(f"成功加载 {len(self.api_docs)} 个API文档")
            
            # 构建对象到函数列表的映射
            self._build_object_function_map()
        except FileNotFoundError:
            print(f"错误: 文件 {file_path} 不存在")
        except json.JSONDecodeError:
            print(f"错误: 文件 {file_path} 不是有效的JSON格式")
        except Exception as e:
            print(f"加载API文档时发生错误: {str(e)}")
    
    def _build_object_function_map(self):
        """构建对象到函数列表的映射"""
        self.object_functions = {}
        for api in self.api_docs:
            function_name = api.get('function_name', '')
            if '.' in function_name:
                # 分割对象名和函数名
                object_name, method_name = function_name.split('.', 1)
                
                # 添加到对象映射
                if object_name not in self.object_functions:
                    self.object_functions[object_name] = []
                self.object_functions[object_name].append(method_name)
    
    def get_functions_by_object(self, object_name: str) -> List[str]:
        """
        根据API所属对象返回所有函数名的列表
        
        参数:
            object_name (str): 对象名称，如"LuaAPI"
            
        返回:
            List[str]: 该对象的所有函数名列表
        """
        if not self.api_docs:
            print("警告: 尚未加载API文档数据")
            return []
            
        # 如果对象存在映射中，直接返回
        if object_name in self.object_functions:
            return self.object_functions[object_name]
            
        # 否则尝试查找匹配的对象
        pattern = re.compile(re.escape(object_name), re.IGNORECASE)
        results = []
        for obj, funcs in self.object_functions.items():
            if pattern.search(obj):
                results.extend(funcs)
        return results
    
    def format_api_to_text(self, api: Dict) -> str:
        """
        将单个API对象转换为描述文本
        
        参数:
            api (Dict): API文档对象
            
        返回:
            str: 格式化的描述文本
        """
        if not api:
            return "未找到API信息"
            
        text = f"函数名: {api.get('function_name', '')}\n"
        text += f"描述: {api.get('description', '')}\n"
        text += f"签名: {api.get('signature', '')}\n"
        
        # 添加参数信息
        parameters = api.get('parameters', [])
        if parameters:
            text += "参数:\n"
            for param in parameters:
                text += f"  {param.get('name', '')}: {param.get('type', '')} - {param.get('description', '')}\n"
        
        # 添加返回信息
        return_info = api.get('return', {})
        if return_info:
            text += f"返回: {return_info.get('type', '')} - {return_info.get('description', '')}"
        
        return text
    
    def search(self, keyword: str) -> List[Dict[str, Union[str, List, Dict]]]:
        """
        根据关键词搜索API文档
        
        参数:
            keyword (str): 搜索关键词
            
        返回:
            List[Dict]: 匹配的API文档列表
        """
        if not self.api_docs:
            print("警告: 尚未加载API文档数据")
            return []
        
        # 编译正则表达式，忽略大小写
        pattern = re.compile(re.escape(keyword), re.IGNORECASE)
        
        results = []
        for api in self.api_docs:
            # 检查函数名是否匹配
            if pattern.search(api.get('function_name', '')):
                results.append(api)
                continue
                
            # 检查描述是否匹配
            if pattern.search(api.get('description', '')):
                results.append(api)
                continue
                
            # 检查签名是否匹配
            if pattern.search(api.get('signature', '')):
                results.append(api)
                continue
                
            # 检查参数名是否匹配
            for param in api.get('parameters', []):
                if pattern.search(param.get('name', '')):
                    results.append(api)
                    break
                    
            # 检查返回类型是否匹配
            return_info = api.get('return', {})
            if pattern.search(return_info.get('type', '')) or pattern.search(return_info.get('description', '')):
                results.append(api)
                continue
        
        return results
    
    def search_by_name(self, name: str) -> List[Dict[str, Union[str, List, Dict]]]:
        """
        根据函数名搜索API文档
        
        参数:
            name (str): 函数名或部分函数名
            
        返回:
            List[Dict]: 匹配的API文档列表
        """
        if not self.api_docs:
            return []
            
        pattern = re.compile(re.escape(name), re.IGNORECASE)
        return [api for api in self.api_docs if pattern.search(api.get('function_name', ''))]
    
    def search_by_description(self, description: str) -> List[Dict[str, Union[str, List, Dict]]]:
        """
        根据描述搜索API文档
        
        参数:
            description (str): 描述关键词
            
        返回:
            List[Dict]: 匹配的API文档列表
        """
        if not self.api_docs:
            return []
            
        pattern = re.compile(re.escape(description), re.IGNORECASE)
        return [api for api in self.api_docs if pattern.search(api.get('description', ''))]
    
    def get_by_name(self, exact_name: str) -> Union[Dict[str, Union[str, List, Dict]], None]:
        """
        根据精确函数名获取API文档
        
        参数:
            exact_name (str): 精确函数名
            
        返回:
            Dict: 匹配的API文档，如果未找到则返回None
        """
        if not self.api_docs:
            return None
            
        for api in self.api_docs:
            if api.get('function_name', '').lower() == exact_name.lower():
                return api
        return None

# 示例用法
if __name__ == "__main__":
    # 创建API文档搜索器
    # searcher = APIDocSearcher()
    
    # # 从JSON文件加载API文档
    # searcher.load_from_json("api_result.json")
    
    # # 示例：获取LuaAPI的所有函数名
    # print("\n=== LuaAPI的所有函数 ===")
    # luaapi_functions = searcher.get_functions_by_object("LuaAPI")
    # for i, func in enumerate(luaapi_functions, 1):
    #     print(f"{i}. {func}")
    
    # # 示例：获取Role的所有函数名
    # print("\n=== Role的所有函数 ===")
    # role_functions = searcher.get_functions_by_object("Role")
    # for i, func in enumerate(role_functions, 1):
    #     print(f"{i}. {func}")
    
    # # 示例：将API对象转换为文本
    # print("\n=== API对象转换为文本 ===")
    # api = searcher.get_by_name("LuaAPI.call_delay_frame")
    # if api:
    #     print(searcher.format_api_to_text(api))
    # else:
    #     print("未找到指定的API")
    
    # # 示例：搜索函数名中包含 'delay' 的API
    # print("\n=== 搜索函数名中包含 'delay' 的API ===")
    # results = searcher.search("delay")
    # for i, api in enumerate(results, 1):
    #     print(f"{i}. {searcher.format_api_to_text(api)}")
    #     print("-" * 50)

    # 1. 从文件读取并保存结果
    html_file = "eggyapi1.html"  # 输入的HTML文件路径
    output_file = "api_result.json"  # 输出的JSON文件路径
    
    html_content = read_html_from_file(html_file)
    if html_content:
        json_result = parse_api_html(html_content)
        if json_result:
            save_json_to_file(json_result, output_file)
    
